﻿// -----------------------------------------------------------------------------------------------
//  <copyright file="TestProjectMigrator.cs" company="http://buginbox.wordpress.com/">
//    Copyright (c) GNU General Public License version 2 (GPLv2)
//  </copyright>
// -----------------------------------------------------------------------------------------------

using System.Collections.Generic;
using System.IO;
using System.Linq;

using Microsoft.TeamFoundation.TestManagement.Client;

namespace TestPlanMigration
{
    /// <summary>
    /// The test project migrator.
    /// </summary>
    public class TestProjectMigrator
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TestProjectMigrator"/> class. Prevents a default instance of the <see cref="TestProjectMigrator"/> class from being created.
        /// </summary>
        /// <param name="sourceTestProject">The source test project.</param>
        /// <param name="destinationTestProject">The destination test project.</param>
        /// <param name="logger">The logger.</param>
        public TestProjectMigrator(TfsProjectTest sourceTestProject, TfsProjectTest destinationTestProject, StreamWriter logger)
        {
            _sourceTestProject = sourceTestProject;
            _destinationTestProject = destinationTestProject;
            _logger = logger;
        }

        /// <summary>
        /// Gets or sets the source test project.
        /// </summary>
        /// <value>
        /// The source test project. 
        /// </value>
        private TfsProjectTest _sourceTestProject { get; set; }

        /// <summary>
        /// Gets or sets the destination test project.
        /// </summary>
        /// <value>
        /// The destination test project. 
        /// </value>
        private TfsProjectTest _destinationTestProject { get; set; }

        /// <summary>
        /// Gets or sets the _logger.
        /// </summary>
        /// <value>
        /// The _logger. 
        /// </value>
        private StreamWriter _logger { get; set; }

        /// <summary>
        /// Writes the specified message.
        /// </summary>
        /// <param name="message">
        /// The message. 
        /// </param>
        private void Write(string message)
        {
            if (_logger == null)
            {
                return;
            }

            _logger.WriteLine(message);
        }

        /// <summary>
        /// Loads the test plans.
        /// </summary>
        /// <returns>
        /// </returns>
        public Dictionary<string, ITestPlan> MigrateTestPlans()
        {
            return MigrateTestPlans(string.Empty);
        }

        /// <summary>
        /// Loads the test plans.
        /// </summary>
        /// <param name="prefix">
        /// The prefix. 
        /// </param>
        /// <returns>
        /// </returns>
        public Dictionary<string, ITestPlan> MigrateTestPlans(string prefix)
        {
            var clonedPlans = new Dictionary<string, ITestPlan>();

            var plans = _sourceTestProject.AllPlans;

            if (plans == null)
            {
                Write("Source project has no plan to clone.");
                return clonedPlans;
            }

            if (prefix == null)
            {
                Write("You must set a prefix (Empty or not) for destination plan's name.");
                return clonedPlans;
            }

            foreach (ITestPlan originalPlan in plans)
            {
                ITestPlan clonedPlan = TfsProjectTest.ClonePlan(_destinationTestProject, originalPlan, prefix);

                if (clonedPlan.RootSuite == null)
                {
                    Write("Cloned plan has no root suite.");
                    return clonedPlans;
                }

                clonedPlans.Add(clonedPlan.Name, clonedPlan);

                if (originalPlan.RootSuite != null)
                {
                    TfsProjectTest.CopySuiteInfo(
                        originalPlan.RootSuite, true, clonedPlan.RootSuite, _destinationTestProject);

                    if (originalPlan.RootSuite.Entries.Count > 0)
                    {
                        MigrateTestSuites(originalPlan.RootSuite, clonedPlan.RootSuite);
                    }
                }
            }

            return clonedPlans;
        }

        /// <summary>
        /// Loads the test suites.
        /// </summary>
        /// <param name="originalParentSuite">
        /// The parent test suite. 
        /// </param>
        /// <param name="targetParentSuite">
        /// The target parent suite. 
        /// </param>
        private void MigrateTestSuites(IStaticTestSuite originalParentSuite, IStaticTestSuite targetParentSuite)
        {
            foreach (ITestSuiteEntry suiteEntry in originalParentSuite.Entries)
            {
                var originalSuite = suiteEntry.TestSuite as IStaticTestSuite;

                if (originalSuite != null)
                {
                    IStaticTestSuite clonedSuite = TfsProjectTest.CloneSuite(_destinationTestProject, originalSuite);
                    targetParentSuite.Entries.Add(clonedSuite);

                    MigrateTestCases(originalSuite, clonedSuite);

                    if (originalSuite.Entries.Count > 0)
                    {
                        MigrateTestSuites(originalSuite, clonedSuite);
                    }
                }
            }
        }

        /// <summary>
        /// Loads the test cases.
        /// </summary>
        /// <param name="originalParentSuite">
        /// The original parent suite. 
        /// </param>
        /// <param name="targetParentSuite">
        /// The target parent suite. 
        /// </param>
        private void MigrateTestCases(IStaticTestSuite originalParentSuite, IStaticTestSuite targetParentSuite)
        {
            // Will bring only the Test Case under a specific Test Suite.
            ITestSuiteEntryCollection originalTestCases = originalParentSuite.TestCases;

            foreach (ITestSuiteEntry testcase in originalTestCases)
            {
                var originalTestCase = testcase.TestCase;
                var targetTestCases =
                    _destinationTestProject.AllTestCases.Where(x => x.Title == originalTestCase.Title).ToList();

                ITestCase targetTestCase = null;

                if (targetTestCases.Count > 0)
                {
                    // If conflict, try with ID
                    targetTestCase = targetTestCases.FirstOrDefault(x => x.Id == originalTestCase.Id);
                }

                if (targetTestCase != null)
                {
                    targetParentSuite.Entries.Add(targetTestCase);
                }
                else
                {
                    Write(
                        string.Format(
                            "Test Case work item with ID {0} (on source) was not attached to suite '{1}' (on target).", 
                            originalTestCase.Id, 
                            targetParentSuite.Title));
                }
            }
        }
    }
}